package nuc.jyg.service.imp;

import nuc.jyg.dao.AppointmentDao;
import nuc.jyg.dao.BookDao;
import nuc.jyg.dto.AppointExecution;
import nuc.jyg.entity.Appointment;
import nuc.jyg.entity.Book;
import nuc.jyg.exception.AppointException;
import nuc.jyg.exception.NoNumberException;
import nuc.jyg.exception.RepeatAppointException;
import nuc.jyg.service.BookService;
import nuc.jyg.util.AppointmentStateEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @Author Nuc YongGuang Ji
 * Created by JiYongGuang on 2017/5/26.
 */
@SuppressWarnings("SpringJavaAutowiringInspection")
@Service
public class BookServiceImp implements BookService {

    private Logger logger = LoggerFactory.getLogger(BookServiceImp.class);
//    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private BookDao bookDao;

    @Autowired
    private AppointmentDao appointmentDao;

    public Book getByID(long bookId) {
        return bookDao.queryById(bookId);
    }

    public List<Book> getList() {
        return bookDao.queryAll(0, 1002);
    }


    /**
     * 使用注解控制事务方法的优点： 1.开发团队达成一致约定，明确标注事务方法的编程风格
     * 2.保证事务方法的执行时间尽可能短，不要穿插其他网络操作，RPC/HTTP请求或者剥离到事务方法外部
     * 3.不是所有的方法都需要事务，如只有一条修改操作，只读操作不需要事务控制
     */
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT,
            readOnly = false, rollbackFor = {RuntimeException.class})
    public AppointExecution appoint(long bookId, long studentId) {
        try {
            // 减库存
            int updateReduceNumber = bookDao.reduceNumber(bookId);
            /*
                @Transaction注解的存在控制事物。引发事物的回滚，
                int i = 1 / 0;
             */
            if (updateReduceNumber <= 0) { // 库存不足
                // return new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);//错误写法
                // 区别:抛异常方式可以触发事物回滚。
                throw new NoNumberException("库存不足");
            } else {
                // 执行预约操作
                int insertAppointment = appointmentDao.insertAppointment(bookId, studentId);
                if (insertAppointment <= 0) { // 重复预约
                    // return new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);//错误写法
                    throw new RepeatAppointException("重复预约");
                } else { // 预约成功
                    Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);
                    return new AppointExecution(bookId, AppointmentStateEnum.SUCCESS, appointment);
                }
            }
            /*
                要先于 catch Exception 异常前先 catch 住再抛出，不然自定义的异常也会被转换为 AppointException，
                导致控制层无法具体识别是哪个异常
             */
        } catch (NoNumberException e1) {
            throw e1;
        } catch (RepeatAppointException e2) {
            throw e2;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            // 所有编译期异常转换为运行期异常
            // return new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);//错误写法
            throw new AppointException("appoint inner error", e.getCause());
        }
    }
}
